#+firstSimulationDay!/usr/bin/env python
#-*- coding: utf-8 -*-

import observers
import evaluate
import evolution
import strategies
from collections import namedtuple

class Evois(object): # {{{
    """
        Fasada do algorytmu ewolucyjnego.
    """

    def __init__(self, initialAsset, ticker, investmentStrategies, evaluationStrategy, 
                 buyRatio, sellRatio, evolutionStrategy, 
                 evolutionObservers = None,
                 resultObservers = None,
                 lastSimulationDay = None, firstSimulationDay = 0):
         
        """
           initialAsset         - początkowa wartość portfela
           ticker               - symbol spółki (dopuszczalne symbole to nazwy plików z data/*.mst, bez rozszerzeń)
           investmentStrategies - lista strategii inwestycyjnych 
           evolutionStrategy    - algorytm ewolucyjny
           evaluationStrategy   - funkcja przystosowania chromosomu 
           buyRatio             - przy sygnale kupna, przeznacz taki procent funduszy na nowe akcje
           sellRatio            - przy sygnale sprzedaży, taką część akcji sprzedaj
           evolutionObservers   - lista obserwatorów algorytmu ewolucyjnego. jako argumenty otrzymują numer iteracji
                                  i ocenę najlepszego osobnika
           evolutionObservers   - lista obserwatorów algorytmu ewolucyjnego. po zakończeniu każdego pokolenia, 
                                  jako argumenty otrzymują numer iteracji i ocenę najlepszego osobnika
           resultObservers      - lista obserwatorów wyników algorytmu ewolucyjnego. po zakończeniu ewolucji,
                                  obserwatorzy dostają chromosom najlepszego osobnika
           lastSimulationDay    - ostatni (według kolejności notowań) dzień symulacji. dla None lub za dużej wartości,
                                  przyjmij maksymalny możliwy dla danych notowań
           firstSimulationDay   - pierwszy dzień symulacji 
        """

        self._initialAsset         = initialAsset
        self._ticker               = ticker
        self.evolutionStrategy     = evolutionStrategy
        self._evaluationStrategy   = evaluationStrategy
        self._investmentStrategies = investmentStrategies
        self.evolutionObservers    = evolutionObservers
        self.resultObservers       = resultObservers


        self._evaluationStrategy.setStockSymbol(ticker, lastSimulationDay, firstSimulationDay)

        self.lastStockData = self._evaluationStrategy.stockModel.lastStockData
        self._evaluationStrategy.setInitialAsset(initialAsset)
        self._evaluationStrategy.setStrategies(investmentStrategies)
        self._evaluationStrategy.setBuyRatio(buyRatio)
        self._evaluationStrategy.setSellRatio(sellRatio)

    def eval(self, arguments):

        # akceptuje named tuples albo zwykłe krotki
        try:
            strategy = self.evolutionStrategy(self._evaluationStrategy, **arguments._asdict())
        except AttributeError:
            strategy = self.evolutionStrategy(self._evaluationStrategy, *arguments)

        for observer in self.evolutionObservers:
            strategy.attachEvolutionObserver(observer)

        for observer in self.resultObservers:
            strategy.attachResultObserver(observer)

        strategy()
# }}}

class IncrementalEvois(object): # {{{
    """
        Fasada do inkrementalnego algorytmu ewolucyjnego.
    """

    def __init__(self, 
                 initialAsset,
                 ticker,
                 investmentStrategies,
                 evaluationStrategy, 
                 buyRatio,
                 sellRatio,
                 evolutionStrategy, 
                 params,
                 intervalIncrement = 7, # co ile dni odpalamy symulację
                 evolutionObservers = None,
                 resultObservers = None,
                 lastSimulationDay = None,
                 firstSimulationDay = 0):
         
        self._initialAsset         = initialAsset
        self._ticker               = ticker
        self.evolutionStrategy     = evolutionStrategy
        self._evaluationStrategy   = evaluationStrategy
        self._investmentStrategies = investmentStrategies
        self.evolutionObservers    = evolutionObservers
        self.resultObservers       = resultObservers
        self.intervalIncrement     = intervalIncrement 
        self.currentLastSimulationDay = intervalIncrement+firstSimulationDay+intervalIncrement
        self.firstSimulationDay    = firstSimulationDay
        self.lastSimulationDay     = lastSimulationDay
        self.currentSimulationDay  = intervalIncrement+firstSimulationDay
        self.params                = params
        self.currentMoney          = initialAsset
        self.currentShares         = 0
        self.buyRatio              = buyRatio
        self.sellRatio             = sellRatio


        self._evaluationStrategy.setStockSymbol(ticker, self.currentLastSimulationDay, firstSimulationDay)
        self.lastSimulationDay = min (self.lastSimulationDay, self._evaluationStrategy.stockModel.lastStockData)

        self._evaluationStrategy.setInitialAsset(initialAsset)
        self._evaluationStrategy.setStrategies(investmentStrategies)
        self._evaluationStrategy.setBuyRatio(buyRatio)
        self._evaluationStrategy.setSellRatio(sellRatio)

    def generateStrategyForNextWeek(self): # {{{
        """
            Pobierając dane giełdowe od firstSimulationDay do currentSimulationDay,
            wyewoluuj i zwróć chromosom strategii inwestycyjnych, na kolejny tydzień
        """

        arguments = self.params

        self._evaluationStrategy.setStockSymbol(self._ticker, self.currentSimulationDay , self.firstSimulationDay)

        try:
            strategy = self.evolutionStrategy(self._evaluationStrategy, **arguments._asdict())
        except AttributeError:
            strategy = self.evolutionStrategy(self._evaluationStrategy, *arguments)

        for observer in self.evolutionObservers:
            strategy.attachEvolutionObserver(observer)

        for observer in self.resultObservers:
            strategy.attachResultObserver(observer)

        strategy = self.evolutionStrategy(self._evaluationStrategy, **arguments._asdict())
        chromosome = strategy()

        return chromosome
    # }}}

    def increaseSimulationRange(self): # {{{
        """
            przesuń koniec symulacji o intervalIncrement wprzód
        """
        self.currentSimulationDay      = self.currentLastSimulationDay
        self.currentLastSimulationDay += self.intervalIncrement

        if (self.currentLastSimulationDay <= self.lastSimulationDay):
            return True
        else:
            return False
    # }}}

    def simulateNextWeek(self, chromosome, money, shares): # {{{
        """
            symuluj grę przez kolejny tydzień, posługując się sygnałami generowanymi przez
            wyewoluowane strategie
        """

        import copy
        self.simulationEvaluator = copy.deepcopy(evaluate.evalAssetsReturn)
        self.simulationEvaluator.setStockSymbol(self._ticker, self.currentLastSimulationDay, self.currentSimulationDay)
        self.simulationEvaluator.setStrategies(self._investmentStrategies)
        self.simulationEvaluator.attachObserver(observers.stdoutObserver)

        self.simulationEvaluator.setInitialAsset(money)
        self.simulationEvaluator.setInitialShares(shares)

        self.simulationEvaluator.setStrategies(self._investmentStrategies)
        self.simulationEvaluator.setBuyRatio(self.buyRatio)
        self.simulationEvaluator.setSellRatio(self.sellRatio)

        fitness = self.simulationEvaluator(chromosome)
        return (self.simulationEvaluator.money, self.simulationEvaluator.shares)
    # }}}

    def run(self): # {{{

        chromosome = self.generateStrategyForNextWeek()
        money, shares = self.simulateNextWeek(chromosome, self.currentMoney, self.currentShares)

        while self.increaseSimulationRange():
            chromosome    = self.generateStrategyForNextWeek()
            money, shares = self.simulateNextWeek(chromosome, money, shares)
    # }}}


# }}}

def evoisTest(): # {{{
    x = Evois ( initialAsset         = 100
              , ticker               = 'cerabud'
              , investmentStrategies = strategies.strategies
              , evolutionStrategy    = evolution.Pbil
              , evaluationStrategy   = evaluate.evalAssetsReturn
              , buyRatio             = 0.5
              , sellRatio            = 0.5
              , evolutionObservers   = [observers.stdoutObserver]
              , resultObservers      = [observers.stdoutObserver, observers.FileObserver('main.dat')]
              , lastSimulationDay    = 5
              )

    
    params = evolution.PbilParams( iterations = 500
                                 , populationSize = 10
                                 , chromosomeLen  = len(strategies.strategies)
                                 , theta1         = 0.05
                                 , theta2         = 0.05
                                 , theta3         = 0.5
                                 )

    x.eval(params)
    # x.eval((10, len(strategies.strategies), 0.05, 0.005, 0.005))
# }}}

def incevoisTest(): # {{{

    evolutionParams = evolution.PbilParams(iterations = 500 
                                         , populationSize = 3
                                         , chromosomeLen  = len(strategies.strategies)
                                         , theta1         = 0.05
                                         , theta2         = 0.05
                                         , theta3         = 0.5
                                         )

    x = IncrementalEvois(
                initialAsset         = 100
              , ticker               = 'cerabud'
              , investmentStrategies = strategies.strategies
              , evolutionStrategy    = evolution.Pbil
              , evaluationStrategy   = evaluate.evalAssetsReturn
              , buyRatio             = 0.5
              , sellRatio            = 0.5
              , params               = evolutionParams
              , intervalIncrement    = 7
              , evolutionObservers   = [] #[observers.stdoutObserver]
              , resultObservers      = [] #[observers.stdoutObserver, observers.FileObserver('main.dat')]
              , lastSimulationDay    = 300
              )

    x.run()
# }}}

if __name__ == "__main__":
    incevoisTest()
# vim: fdm=marker
